home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------------------------------------
- JuggleRoutines.c
-
- Routines that manipulate the throws data, and high level routines
-
- ----------------------------------------------------------------------*/
-
- #include "PaperJuggling.h"
-
- //----------------------------------------------------
- // Init and CleanUp
-
- OSErr InitJuggle(WindowPtr wind, short numJugglers, short numCounts)
- {
- HandHandle throwsHandle;
- long throwsSize;
- short count;
- JuggleHandle aJuggle;
-
- aJuggle = GetWindowJuggle(wind);
- if(aJuggle == nil)
- return -1;
-
- // Try to allocate the throws
- throwsSize = (numCounts * numJugglers * sizeof(Hand));
- throwsHandle = (HandHandle) NewHandleClear(throwsSize);
- if(throwsHandle == nil)
- return -108;
-
- // Keep the throws
- (*aJuggle)->theThrows = throwsHandle;
-
- // Set up initial screen map
- for(count = 0; count < numJugglers; count++)
- {
- (*aJuggle)->jugglerMap[count] = count + 1;
- }
-
- // State variables
- (*aJuggle)->gridPt.x = kDefaultGridSpace;
- (*aJuggle)->gridPt.y = kDefaultGridSpace;
- (*aJuggle)->numJugglers = numJugglers;
- (*aJuggle)->numCounts = numCounts;
-
- // Set the contentSize of the doc
- ResetJuggleContentSize(aJuggle);
-
- // Set up the graphics
- if(SetUpJuggleGraphics(wind, aJuggle) == nil)
- {
- DisposeHandle((Handle)throwsHandle);
- (*aJuggle)->theThrows = nil;
- }
- return noErr;
- }
-
- void CleanUpJuggle(WindowPtr window, Boolean graphicsToo)
- {
- JuggleHandle aJuggle = GetWindowJuggle(window);
-
- if(graphicsToo)
- CleanUpJuggleShapes(window);
- DisposeHandle((Handle)(*aJuggle)->theThrows); // Dispose of throws data
- }
-
- //----------------------------------------------------
- // Accessor routine for the juggle
-
-
- JuggleHandle GetWindowJuggle (WindowPtr wind)
- {
- JuggleHandle jug = nil;
-
- if ( wind != nil )
- jug = (JuggleHandle) GetWRefCon(wind);
-
- return jug;
- }
-
-
- //----------------------------------------------------
- // throw manipulation routines
-
- // Resize the throws in the juggle, copying over as many throws as possible if indicated.
- // Returns true if there are no errors
- Boolean ResizeThrows(JuggleHandle aJuggle, short newJugglers, short newCounts,
- Boolean copyThrows)
- {
- short oldJugglers, oldCounts;
- long throwsSize;
- HandHandle newThrows, oldThrows;
-
- oldJugglers = (*aJuggle)->numJugglers;
- oldCounts = (*aJuggle)->numCounts;
- oldThrows = (*aJuggle)->theThrows;
-
- // Range checking
- if(newJugglers == oldJugglers && newCounts == oldCounts)
- return true; // no change, do nothing
- if(newJugglers > kMaxJugglers || newJugglers < 1 || newCounts < 2)
- return false;
-
- // allocate the new throws
- throwsSize = (newCounts * newJugglers * sizeof(Hand));
- newThrows = (HandHandle) NewHandleClear(throwsSize);
- if( newThrows == nil )
- return false;
-
- // if copyThrows is true, copy over what throws we can
- if(copyThrows == true)
- {
- CopyThrows(oldThrows, oldJugglers, oldCounts,
- newThrows, newJugglers, newCounts);
- }
-
- // Dispose old throws
- DisposeHandle((Handle)oldThrows);
-
- // update the juggle
- (*aJuggle)->numJugglers = newJugglers;
- (*aJuggle)->numCounts = newCounts;
- (*aJuggle)->theThrows = newThrows;
-
- return true;
- }
-
- // Fast, no range checking, most routines call GetHand (below) instead
- HandPtr GetHandPtr(HandHandle throws, short numCounts, short juggler, short count)
- {
- HandPtr result = *throws;
-
- // increment the right amount to get to the hand we want
- return (result + ((juggler - 1) * numCounts) + (count - 1) );
- }
-
- // Given the HandLoc, return a pointer to the specific hand
- HandPtr GetHand(JuggleHandle aJuggle, HandLoc theLoc, Boolean wrapIt)
- {
- // Make sure we're in range, wrapping if indicated
- if(theLoc.time > (*aJuggle)->numCounts)
- {
- if(wrapIt)
- {
- theLoc.time %= (*aJuggle)->numCounts;
- // could be zero, if the input was a multiple of numCounts.
- // if so, make it numCounts instead
- if(theLoc.time == 0)
- theLoc.time = (*aJuggle)->numCounts;
- }
- else
- return nil;
- }
- else if(theLoc.time < 1)
- return nil;
- if(theLoc.juggler > (*aJuggle)->numJugglers || theLoc.juggler < 1)
- return nil;
-
- return GetHandPtr((*aJuggle)->theThrows, (*aJuggle)->numCounts, theLoc.juggler, theLoc.time);
- }
-
- // Given the time and juggler, return a pointer to the specific hand
- HandPtr GetIndexedHand(JuggleHandle aJuggle, short time, short juggler, Boolean wrapIt)
- {
- HandLoc aLoc;
-
- aLoc.time = time;
- aLoc.juggler = juggler;
-
- return GetHand(aJuggle, aLoc, wrapIt);
- }
-
- // Add the throw to the connections data
- void AddThrow(JuggleHandle aJuggle, HandLoc from, HandLoc to)
- {
- HandPtr aHand;
-
- // set the sink of the source juggler
- aHand = GetHand(aJuggle, from, false);
- if(aHand != nil)
- aHand->sink = to;
-
- // set the source of the sink juggler
- aHand = GetHand(aJuggle, to, false);
- if(aHand != nil)
- aHand->source = from;
- }
-
- // Convenience routine that takes the four individual coords
- void AddIndexedThrow(JuggleHandle aJuggle,
- short fromTime, short fromJuggler,
- short toTime, short toJuggler )
- {
- HandLoc from, to;
-
- from.time = fromTime;
- from.juggler = fromJuggler;
- to.time = toTime;
- to.juggler = toJuggler;
-
- AddThrow(aJuggle, from, to);
- }
-
- // Remove the throw from the connections data
- void RemoveThrow(JuggleHandle aJuggle, HandLoc from, HandLoc to)
- {
- HandPtr aHand;
- HandLoc noLoc = {0, 0};
-
- // remove the sink of the source juggler
- aHand = GetHand(aJuggle, from, false);
- if(aHand != nil)
- aHand->sink = noLoc;
-
- // remove the source of the sink juggler
- aHand = GetHand(aJuggle, to, false);
- if(aHand != nil)
- aHand->source = noLoc;
- }
-
- // Convenience routine that takes the four individual coords
- void RemoveIndexedThrow(JuggleHandle aJuggle,
- short fromTime, short fromJuggler,
- short toTime, short toJuggler )
- {
- HandLoc from, to;
-
- from.time = fromTime;
- from.juggler = fromJuggler;
- to.time = toTime;
- to.juggler = toJuggler;
-
- RemoveThrow(aJuggle, from, to);
- }
-
- // Remove all the throws that touch this juggler from the connections data
- void ClearJugglerThrows(JuggleHandle aJuggle, short juggler)
- {
- short count;
- HandPtr hand;
-
- for(count = 0; count < (*aJuggle)->numCounts; count++)
- {
- hand = GetIndexedHand(aJuggle, count + 1, juggler, false);
- if(hand != nil)
- {
- // if there's a source, remove it
- if(hand->source.time != 0)
- RemoveIndexedThrow(aJuggle, hand->source.time, hand->source.juggler,
- count + 1, juggler);
- // if there's a sink, remove it
- if(hand->sink.time != 0)
- RemoveIndexedThrow(aJuggle, count + 1, juggler,
- hand->sink.time, hand->sink.juggler);
- }
- }
- }
-
- // Copy throws data from one block of hands to another, preserving what we can.
- // The new throws are assumed blank to begin with.
- void CopyThrows(HandHandle oldThrows, short oldJugglers, short oldCounts,
- HandHandle newThrows, short newJugglers, short newCounts)
- {
- short thisJuggler, thisTime, jugglerMax, countMax;
-
- // Step through the "intersection" of the two blocks, and only copy throws that are
- // entirely in that block
- jugglerMax = min(oldJugglers, newJugglers);
- countMax = min(oldCounts, newCounts);
- for(thisJuggler = 1; thisJuggler <= jugglerMax; thisJuggler++)
- {
- for(thisTime = 1; thisTime <= countMax; thisTime++)
- {
- HandPtr oldHand, newHand;
-
- oldHand = GetHandPtr(oldThrows, oldCounts, thisJuggler, thisTime);
- newHand = GetHandPtr(newThrows, newCounts, thisJuggler, thisTime);
-
- // if there's a throw from here to somewhere in range, copy it
- if(oldHand->sink.time > 0 &&
- oldHand->sink.time <= countMax && oldHand->sink.juggler <= jugglerMax)
- {
- HandLoc sink = oldHand->sink;
-
- // Copy the sink to the new hand
- newHand->sink = oldHand->sink;
-
- // Set the source of the sink, too.
- oldHand = GetHandPtr(oldThrows, oldCounts, sink.juggler, sink.time);
- newHand = GetHandPtr(newThrows, newCounts, sink.juggler, sink.time);
- newHand->source = oldHand->source;
- }
- }
- }
- }
-
- // Find the nearest available receiver for a throw originating from the given coords
- // Available means no existing source
- HandLoc FindReceiver(JuggleHandle aJuggle, HandLoc from)
- {
- short time, juggler, maxTime;
- HandLoc result = {0, 0};
- HandPtr hand;
-
- // --First try the self throw
-
- // Get our "next" hand
- time = from.time + 1; // next count
- juggler = from.juggler; // "us"
- hand = GetIndexedHand(aJuggle, time, juggler, true);
- if(hand != nil)
- {
- if(hand->source.time == 0) // if not already receiving, this is it
- {
- result.time = time;
- result.juggler = juggler;
- }
- else // already receiving, try to "reply" to the same juggler
- {
- // now we know who to try to throw to, search forward through time
- juggler = hand->source.juggler;
- if((*aJuggle)->numCounts > 4) // limit to a quad for a reply
- maxTime = from.time + 4;
- else
- maxTime = from.time + (*aJuggle)->numCounts; // one wrap, no more
- for(time = from.time + 1; time <= maxTime; time++)
- {
- hand = GetIndexedHand(aJuggle, time, juggler, true);
- if(hand != nil && hand->source.time == 0)
- {
- // This is it, save it
- result.time = time;
- result.juggler = juggler;
- break;
- }
- }
- }
- }
-
- if(result.time == 0) // Nothing yet
- {
- // OK, do exhaustive search. Look forward in time, up to maxTime
- maxTime = from.time + (*aJuggle)->numCounts; // one wrap, no more
- for(time = from.time + 1; time <= maxTime; time++)
- {
- // and at every juggler, starting with 1
- for(juggler = 1; juggler <= (*aJuggle)->numJugglers; juggler++)
- {
- hand = GetIndexedHand(aJuggle, time, juggler, true);
- if(hand != 0 && hand->source.time == 0)
- {
- // This is it, save it
- result.time = time;
- result.juggler = juggler;
- break;
- }
- }
- if(result.time != 0)
- break; // bail if we're done
- }
- }
- return result;
- }
-
-
-
- void MoveJuggler(JuggleHandle aJuggle, short whichJuggler, short offset)
- {
- // the juggler moved vertically: adjust jugglerMap and graphics
- if(offset != 0)
- {
- short startRow, newRow;
-
- // Figure out the row we're in
- startRow = JugglerToRow(aJuggle, whichJuggler);
-
- // Figure out the row we're going to
- newRow = startRow + offset;
-
- // Redo the jugglerMap
- MoveJugglerInMap(aJuggle, whichJuggler, startRow, newRow);
-
- // Redo the juggler picture
- MoveJugglerInPict(aJuggle, startRow, newRow);
-
- // caller should rebuild throws pict and force a redraw
- }
- }
-
- Boolean AddJuggler(JuggleHandle aJuggle)
- {
- short count, newRow; // same as new juggler number
- gxShape newJuggler = nil, aSep = nil;
- gxLine theLine;
-
- if(aJuggle == nil)
- return false;
-
- // Get the new row
- newRow = (*aJuggle)->numJugglers + 1;
-
- // Try to resize the throws.
- if(ResizeThrows(aJuggle, newRow, (*aJuggle)->numCounts, true) == false)
- return false;
-
- // Make the juggler and separator
- // !!! This should probably be a separate routine (AddJugglerGraphics?)
- newJuggler = MakeJugglerShape(aJuggle, true, newRow);
- aSep = MakeSeparatorShape(aJuggle, true);
- if(newJuggler == nil || aSep == nil)
- {
- // Back out, resizing back down again, and disposing any shapes
- ResizeThrows(aJuggle, newRow - 1, (*aJuggle)->numCounts, true);
- if(newJuggler != nil)
- GXDisposeShape(newJuggler);
- if(aSep != nil)
- GXDisposeShape(aSep);
- return false;
- }
-
- // All the allocation worked, now update the juggler graphics.
-
- // set juggler's position and add it to the picture
- GXMoveShape(newJuggler, 0, (*aJuggle)->gridPt.y * (newRow - 1));
- InsertPictureItem((*aJuggle)->jugglersPict, newRow, newJuggler, nil, nil, nil);
-
- // Same with separator
- GXMoveShape(aSep, 0, (*aJuggle)->gridPt.y * (newRow - 2));
- AddToPicture((*aJuggle)->sepsPict, aSep, nil, nil, nil);
-
- // Extend vertical separators
- for(count = 1; count <= kNumVSeparators; count++)
- {
- GetPictureItem((*aJuggle)->sepsPict, count, &aSep, nil, nil, nil);
- GXGetLine(aSep, &theLine);
- theLine.last.y += (*aJuggle)->gridPt.y;
- GXSetLine(aSep, &theLine);
- }
-
- // Add the new juggler to the screen map
- (*aJuggle)->jugglerMap[newRow - 1] = newRow;
-
- // Make sure any changes in the throws are shown.
- RebuildThrowsPict(aJuggle);
-
- // Set the content size appropriately
- ResetJuggleContentSize(aJuggle);
-
- // Mark dirty
- (*aJuggle)->dirty = true;
-
- return true;
- }
-
- // Just creates a new window and copies throws data over, returning the new window
- WindowPtr ResizeJuggle(WindowPtr wind)
- {
- WindowPtr newWind;
- JuggleHandle oldJuggle, newJuggle;
- Str255 title;
-
- if(wind == nil) // safety check
- return nil;
-
- // Set up a new juggle
- newWind = AppNew(0, 0); // Ask user how big to make it
- if(newWind == nil)
- return nil;
-
- // Copy over window name
- GetWTitle(wind, title);
- SetWTitle(newWind, title);
-
- // Get the two juggle structures
- oldJuggle = GetWindowJuggle(wind);
- newJuggle = GetWindowJuggle(newWind);
-
- // Copy throws data from the old one to the new one
- CopyThrows((*oldJuggle)->theThrows, (*oldJuggle)->numJugglers, (*oldJuggle)->numCounts,
- (*newJuggle)->theThrows, (*newJuggle)->numJugglers, (*newJuggle)->numCounts);
-
- // Make sure any changes in the throws are shown.
- RebuildThrowsPict(newJuggle);
-
- // Copy over the fileSpec
- (*newJuggle)->fileSpec = (*oldJuggle)->fileSpec;
-
- // Throw away old one
- AppClose(wind);
-
- // Mark new one dirty
- (*newJuggle)->dirty = true;
-
- return newWind;
- }